//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package roadmap_dao

import (
	"context"
	"fmt"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/roadmap_user_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type NoteInfoDbItem struct {
	NoteId       string `bson:"_id"` //自动生成
	RoadmapId    string `bson:"roadmapId"`
	NodeId       string `bson:"nodeId"`
	UserId       string `bson:"userId"`
	Content      string `bson:"content"`
	RoadmapTitle string `bson:"roadmapTitle"`
	NodeTitle    string `bson:"nodeTitle"`
	TimeStamp    int64  `bson:"timeStamp"`
}

func (item *NoteInfoDbItem) ToNoteInfo() *roadmap_user_api.NoteInfo {
	return &roadmap_user_api.NoteInfo{
		RoadmapId:    item.RoadmapId,
		NodeId:       item.NodeId,
		Content:      item.Content,
		RoadmapTitle: item.RoadmapTitle,
		NodeTitle:    item.NodeTitle,
		TimeStamp:    item.TimeStamp,
	}
}

type _NoteDao struct {
	mc *mongo.Collection
}

func newNoteDao(db *mongo.Database) (*_NoteDao, error) {
	mc := db.Collection(db_ns.DB_NS_ROADMAP + "note")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys: bson.D{{"userId", 1}, {"roadmapId", 1}},
		},
		{
			Keys: bson.D{{"userId", 1}, {"timeStamp", 1}},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_NoteDao{mc: mc}, nil
}

func (db *_NoteDao) Upsert(ctx context.Context, dbItem *NoteInfoDbItem) error {
	dbItem.NoteId = db.genNoteId(dbItem.UserId, dbItem.RoadmapId, dbItem.NodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": dbItem.NoteId}, bson.M{"$set": dbItem},
		options.Update().SetUpsert(true))
	return err
}

func (db *_NoteDao) genNoteId(userId, roadmapId, nodeId string) string {
	return fmt.Sprintf("%s:%s:%s", userId, roadmapId, nodeId)
}

func (db *_NoteDao) Exist(ctx context.Context, userId, roadmapId, nodeId string) (bool, error) {
	noteId := db.genNoteId(userId, roadmapId, nodeId)
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": noteId})
	return count > 0, err
}

func (db *_NoteDao) Get(ctx context.Context, userId, roadmapId, nodeId string) (*NoteInfoDbItem, error) {
	noteId := db.genNoteId(userId, roadmapId, nodeId)
	retItem := &NoteInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": noteId}).Decode(retItem)
	return retItem, err
}

func (db *_NoteDao) Remove(ctx context.Context, userId, roadmapId, nodeId string) error {
	noteId := db.genNoteId(userId, roadmapId, nodeId)
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": noteId})
	return err
}

func (db *_NoteDao) CountByUser(ctx context.Context, userId string, filterByRoadmapId bool, roadmapId string) (uint32, error) {
	query := bson.M{"userId": userId}
	if filterByRoadmapId {
		query["roadmapId"] = roadmapId
	}
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_NoteDao) ListByUser(ctx context.Context, userId string, filterByRoadmapId bool, roadmapId string, offset, limit int64) ([]*NoteInfoDbItem, error) {
	query := bson.M{"userId": userId}
	if filterByRoadmapId {
		query["roadmapId"] = roadmapId
	}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSkip(offset).SetLimit(limit).SetSort(bson.M{"timeStamp": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*NoteInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_NoteDao) ListByRoadmap(ctx context.Context, roadmapId string, filterByNodeId bool, nodeId string) ([]*NoteInfoDbItem, error) {
	query := bson.M{"roadmapId": roadmapId}
	if filterByNodeId {
		query["nodeId"] = nodeId
	}
	cursor, err := db.mc.Find(ctx, query)
	if err != nil {
		return nil, err
	}
	retList := []*NoteInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_NoteDao) ListSimpleRoadmapInfo(ctx context.Context, userId string) ([]*NoteInfoDbItem, error) {
	query := bson.M{"userId": userId}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetProjection(bson.M{"roadmapId": 1, "roadmapTitle": 1}).SetSort(bson.M{"timeStamp": -1}))
	if err != nil {
		return nil, err
	}
	tmpList := []*NoteInfoDbItem{}
	err = cursor.All(ctx, &tmpList)
	if err != nil {
		return nil, err
	}
	roadmapIdMap := map[string]bool{}
	retList := []*NoteInfoDbItem{}
	for _, item := range tmpList {
		if roadmapIdMap[item.RoadmapId] {
			continue
		}
		roadmapIdMap[item.RoadmapId] = true
		retList = append(retList, item)
	}
	return retList, nil
}
